- web6047 - (2021/09/10(金) 現在、システム調整中のため、一部の表示がおかしいかもしれません)

web6047 2021年

プログラミングやRPG(作るほう)が好きな人の日記

このウェブページは毎日 夜11時にアクセスできなくなります。

朝6時半に再開されます。(世の中のネット依存対策として)

例外でアクセスができる場合があります。上のメニューの「aboutThisWebsite」を参照してください。


以下の表は、このウェブページの管理人のパソコンの使用時間を管理・制限するためのものです。

覚え書き: プログラミングの途中で、「D. 予定 終了時刻」になったとき、今行っているプログラムについて考えてメモを取るのはOK。しかし、考えを形にするのはNG。





































NO PC WEEK に代わる PC 使用制限のしくみ(新β版)
No. A1.
運動
開始時
A2.
1問
A3.
運動
終了時
H1. 予定
作業内容
H2. 予定
作業詳細(進捗%)
判定×の理由
B. 実際
開始時刻
C. 予定
使用時間
(当日全)
D. 予定
終了時刻
E. 実際
終了時刻
F. 実際
使用時間
G. 判定
◎ 9分以下
○ 10分~19分
△ 20分~29分
× 30分以上
397 全体的・基本・簡潔のRPG コマンドメニュー作成(70%)
もし時間内に終わらない場合は:あきらめる
20:50 1:30(1:30) 22:20 22:25 1:35
396 グリフォンモデリング 色付けなど行い、完成させる(45%)
もし時間内に終わらない場合は:あきらめる
18:00 1:00(3:00) 19:00 19:23 1:23
395 全体的・基本・簡潔のRPG コマンドメニュー作成(40%)
もし時間内に終わらない場合は:あきらめる
14:35 1:00(3:00) 15:35 15:38 1:03
394 全体的・基本・簡潔のRPG コマンドメニュー作成(20%)
 バグ1件直し(100%)
もし時間内に終わらない場合は:あきらめる
11:25 1:00(3:00) 12:25 12:36 1:11
393
全体的・基本・簡潔のRPG コマンドメニュー作成(20%)
もし時間内に終わらない場合は:あきらめる
20:15 2:00(4:00) 22:15 22:25 2:10
392 全体的・基本・簡潔のRPG 試作を現在の開発中のものに適用する(中止)
フレキシブルに切り替えるのは問題が出たのでやめて、安直なやり方にする。(部品クラスに本体クラスへの参照を入れる)
もし時間内に終わらない場合は:あきらめて停止し、次に繰り越し
15:45 2:00(4:00) 17:45 17:53 2:08
391
全体的・基本・簡潔のRPG 画面切り替えの試作。(100%)
もし時間内に終わらない場合は:あきらめる
21:10 1:30(1:30) 22:40 22:47 1:37
ルール追加:
「もし時間内に終わらない場合は」を作業詳細に記入する。
それもできず判定×になった場合は、当面(今の仕事を続けている間)平日のPC利用を禁止する。
390
全体的・基本・簡潔のRPG クラスを使用せず、オブジェクト(変数群と関数群を束ねるだけでクラスのしくみがない)を使用して、画面切り替えの試作をしてみる。
×オブジェクトへ束ねた関数の中のthisはそのオブジェクトになるのは予想外だった。本体クラスのキー入力メソッドや画面描画メソッドをフレキシブルに切り替えたい。うまくいかないと時間ばかり食う。
21:50 1:30(1:30) 23:20 24:26 2:36 ×
389
グリフォンモデリング ポーズ保存(100%)
目の調整(0%)
指先にボーンを入れる(100%)
くびすじ調整(0%)
色変更(0%)
×少してこずった
21:30 1:00(2:00) 22:30 23:43 2:13 ×
388
全体的・基本・簡潔のRPG コマンドメニュー試作(30%)
×区切りが悪かった
13:20 1:00(2:00) 14:20 15:00 1:30 ×
387
全体的・基本・簡潔のRPG スクロールの不具合、デバッグ後の整頓(100%) 20:50 2:00(4:00) 22:50 23:02 2:12
386
全体的・基本・簡潔のRPG キャラクター歩行アニメ(100%)
コマンドメニュー(0%)
スクロールの不具合発見し、デバッグ(100%)
×なかなかデバッグできなかった
14:50 2:00(4:00) 16:50 18:15 3:25 ×
385
全体的・基本・簡潔のRPG スクロール処理を行う場所を変更(100%)
コマンドメニューの下地(100%)
21:30 1:30(1:30) 23:00 0:00 2:30 ×
384
全体的・基本・簡潔のRPG 「画面切り替え試作」を本プログラムに適用する(100%) 22:00 1:30(1:30) 23:30 23:43 1:43
383
全体的・基本・簡潔のRPG アニメーション部分の試作(100%) 21:30 1:30(1:30) 23:00 24:00 2:30 ×
382
記事をカテゴリ分けする 記事に対し、キーワードを2つ以上付けられるようにする(100%) 21:25 1:30(1:30) 22:55 23:07 1:42
381
グリフォンモデリング 適宜進める(100%) 17:35 1:30(2:30) 19:05 19:40 2:05 ×
380
3Dお姉さんプログラム説明 上のほうに位置する要素へ向かってスクロールするとき、要素が画面に表示されたときに展開するタイプだと、スクロール終了位置が狂う(50%) 14:20 1:00(2:30) 15:20 15:19 0:59
379
試作 setInterval以外のアニメーションプログラム検証(100%)
※判定は×だけど、21時以降PCを使わないでいられるのは、以前に比べたらすごいこと
15:20 2:00(4:00) 17:20 21:33 6:13 ×

この表の意図:

多くの人はパソコンのやりすぎやネットゲームのやりすぎには困っていると思います。

参考に言うと、この表を使う前の私は 1 回の PC 使用時間がノンストップで 17 時間というときもあったし、平均で言うと毎日 9 時間はやっていたと思います。

この表を使ってパソコンの使用時間を 事前に決めてネット上に公開 することで、パソコンのやりすぎを防止できたら、と思います。

また、数年前から考えてきましたが、そういう徹夜とか長時間作業をするよりも、昼間の短時間作業のほうが生産性は高いのではないかと思います。そういう意味でも期待しています。

※以前は NO PC WEEK と称してパソコンを使用しない期間を設けることでやりすぎに対処してきましたが、もっと具合の良い方法はないかと考え、この表を使うようになりました。

記入の法則:

  • 日付は表示していません(私の生活パターンをすべて知らせるのはよくないから)。しかし、白と灰色の色分けは、同じ色の連続で同じ日を表しています。
  • 左端の「A1. 運動」、「A2. 1問」、「A3. 運動」について
    この表の目的とは異なりますが、ついでとして、遊び100%の毎日を送るときでも勉強の習慣を忘れないために、たった1問で良いので解くことにします。
    正直言うと毎回遊ぶ前に必ず1問勉強するのは心が折れそうです。でも慣れさえすれば…と思います。
    行ったら◎、行わなかったら× を記入します。
    2020年11月20日ぐらいから「A1. 運動」を追加しました。パソコンを行う前に運動することを強制するものです。腕立て伏せ10回とか腹筋10回とかです。
    (ホントだったら30回くらいはやりたいところですが、私は体の調子が悪いので、10回程度にしています)
    2021年6月26日「A3. 運動」を追加。PC使用の終了時にも運動する。
    A1はPC漬けによる筋力衰え対策の意味で、筋トレを行い、
    A3はPC作業でこった体をほぐす意味で、ラジオ体操やストレッチ体操を行う。
  • 右端の「G. 判定」について
    「D. 予定終了時刻」と「E. 実際終了時刻」を比べて、オーバーした時間によって判定を行います。
    ◎ 9分以下
    ○ 10分~19分
    △ 20分~29分
    × 30分以上 (オーバー理由を記載する。理由の統計を取れば何が問題なのか把握でき、改善しやすいです)
  • 平日の 1.5h、2.0h の PC 使用の連続が負担になっているので、週のスケジュールは下記のようにする。
    月水で PC 使用しないことでうまいぐあいに「体力回復」されて、「生活」がうまく回り、プログラミング以外の「創作活動」(イラスト等)に時間が取れることを期待します。
    月:0h
    火:1.5h
    水:0h
    木:1.5h
    金:1.5h
    土:4hまで(休日で、翌日も休日)
    日:2hまで(休日で、翌日平日) ←早起きなおかつ(日々の買い物とかじゃなく)散歩するなら 3h やって良い
    (※2021年2月2日:週の各時間を調整しました)
  • ×、△、"記録しなかった長時間作業" が多いと思ったら、バランスをとるために、NO PC WEEK※ を1週間実施する。
    NO PC WEEK とは私自身の健康のために私のパソコンの使用を制限する期間です)


ちなみに、分単位で記録を取ったりして、だいぶマメに見えるかもしれませんが、Windows の日本語入力(MS-IME)で「いま」と入力し、 変換 キーを押さずに ボタンを押すと現在の時刻になります。道具の便利さが人をマメに見せるのかもしれません。

例外事項:

  • 「E. 実際終了時刻」のあと、プログラミングの場合のみスッパリ終了しないで、今後のプログラミングの方針をテキストファイルに書くのは OK にしています。

中途結果:

結構いい結果になっています。炊事や掃除、散歩、早起きなどが好ましいリズムでできるようになりました。

(2020年9月6日追記:散歩、早起きは最近あまりできていません。掃除や炊事は理想的にできています)

また、パソコン以外の趣味も進むようになりました。電子回路、ガンプラ、RPG のプログラム以外のモンスターイラストやストーリーなど RPG の肉付け部分の創作、勉強、昔好きだったペーパークラフト等々。

そしてパソコンの趣味自体も深夜遅くまで行うよりも質が高くなったように感じます。制限された短い時間の中で結果を得ようとするので、取捨選択が行われているし、時間が終了して、空いた時間ができ、それがほどよい休憩になり、今後の作業の方針を落ち着いて検討することもできます。それが質につながっているのかなと思います。

この取り組みが、15 才 ~ 18 才くらいまでの高専(中退)に所属していた時に実施できていたら良かっただろうなと思います。でもそれくらいの年齢では経験が浅く、このような効果的なルール作りを行うことはできなかったと思います。自分は人に比べて「創作意欲」や「ゲームで遊ぶ欲望」におぼれやすいところがあり、そのコントロールはとても難しいです。


自分の家族にすすめたい方へ:


(これは イラストAC の無料素材です)

パソコンのやりすぎやネットゲームのやりすぎは社会問題にもなっているので、「うちの子についてなんとかしないと…」と思っているご家族の方は多くいらっしゃると思います。

私の両親も過去に私について問題にしていました。学校へ行かず、毎日朝までパソコンに向かい、悶々としていたんです。


この表はその家族が困っていたときから 30 年後に、私が自分で必要を感じて作ったものです。

私は今 一人暮らしをしていて、自分で生計を立てる中、パソコンにおぼれた生活をすると、生活がうまく回らなくなるんです。

具体的には、

  • 人前で疲れた顔を見せてしまい、人間関係がうまくいかなくなる。もっと具体的には、職場、とこや、お店のレジ、歯医者。
  • 掃除、洗濯、炊事が後回しになり、実質、それらを行う時間がなくなってしまう。深夜遅くや翌日に回したり、行わなかったりする。

これらを改善するために表を作りました。


でも、このような必要にせまられて「自分の動機で始めた場合」と、「人からすすめられて始めた場合」とでは、結果が異なると思います。

自分の切実な動機で始めたなら自分から進んでこの表を活用すると思いますが、外から押し付けられたものはなかなか定着しないものです。

あまり適当なことは言えませんが、「中途結果」タブの中の青い部分で書いたことは、本人にとって得になることなので、「ときどき休憩して、他のあの趣味やってみたらどうだ?」とか「ときどき休憩したほうがプログラミングの質が上がるって話だぞ?」という形ですすめてみたらどうでしょうか。(それでも最終的には自立してもらうことは必要だと思いますが)


私が両親を困らせていたときに、突然、外へ一人で出て行って、一人暮らしを始めたり、接客業を始めたり、いくつか資格取得したりといろいろ行えた理由というのは、正直言ってわかりません。(※しかし途中で失業して2度、実家に戻ったことがあります。1 回目は 21 才くらいのときに 5 年間、2 回目は 35 才くらいのときに1年未満、実家にいて、何もしてなかったり働いたりしていました)

私が両親を困らせていたのは 16 才 ~ 20 才くらいの学生のころですが、そのころ家族と私自身と友人たちがみんなそれぞれ、私の生活について心配したり困ったり悩んだり、あの手この手を試したりしていました。そういう煮詰まったような状況が運命をそのように(解決の方向へ)動かすのかもしれません。運命がどうの というのは変ですが、そのくらいのことしか言えません。何かしら取り組む必要があるということですかね。


この社会問題はクリアーすべきものみたいです。



以下は私のための覚え書きです。(装飾はふせん紙のつもりです)

記事の先頭に5W1H を書くと良い。
面白くないゲームは、”変化がないから面白くない”。(あるゲームソフトを見ていてそう思った)

開発時の発想=変化=面白い
 …開発時に何をどう描くかと発想することができれば、作品に変化が生まれ、視聴者が面白いと思うことができる。

開発時に発想できない=変化を作れない=面白くない
 …開発時に発想できなければ、作品に変化を与えることができず、視聴者に面白いと思わせることができない。

(いかに発想するのかはまた別の話)
 
ただし、変化を作りさえすれば面白くなるのかと言ったらそうではないだろう。
人が面白いと思う変化って何?

以下の表は、今月の私の関心事の記録です。

記入日付ジャンル現在タイトル、評価、感想
5月18日(火) アニメ 観ている最中 「はじめの一歩」(リンクは第1話)GYAO!にて無料放送中
週刊少年マガジン連載アニメ化作品/00:24:03 × (75+26+25)話/2000年/公評価:★★★★☆、私評価:★★★★☆
** 感想 **
第一話で、一歩が鷹村に「プロボクサーになりたい」と訴えている場面で、泣けてきた。私自身のいろいろなことに対する思いの強さが、その場面とどこか一致している気がして。
各話観終わると、「おもしろいなぁ」とつぶやいてしまう。
4月2日 (金) アニメ 観ている最中 「ドラゴンボール超」(リンクは第97話)GYAO!にて無料放送中
ジャンプ漫画アニメ化作品/0:23:20 ×131話/2015年/公評価:★★★☆☆、私評価:★★★☆☆
** 感想 **
全王様の「力の大会」が始まって、戦い始めたところです。
ストーリーは鳥山明氏が大部分考えているので、おもしろいです。
「ジョジョ」の仗助編が終わってしまって、他に観るものがこの「超」と超じゃない「ドラゴンボール」しかない…
5月18日(火)

(「ドラゴンボール超」について追記)
「第102話 愛の力が大爆発!?第2宇宙の魔女っ子戦士!!」のレビューで、
「40を過ぎた年寄りが観るのがいけないのかもしれないが、こういう演出というか描写にはもうついていけない。」
とおっしゃっている方がいました。
確かに、鳥山明のノリのようでいて、結構他人の人が鳥山明氏のマネをしているノリだったりする(少し外している)部分も多くあるので、その辺は不満に感じるかもしれません。


以降は今月の日記(このウェブページのメイン)です。上ほど新しい日記です。


2021/6/27(日)

グリフォンモデリング -[modeling]

笠倉出版社「見てわかる!世界のドラゴン&モンスター案内」で説明されている色合いにしました。


目を赤にして、翼を青にしました。

翼は青か白と説明されているので、青光りってことかな。

次回、翼を白色に発光させてみます。


2021/6/22(火)

日立市のクラウドファンディングに寄付

日立駅前の桜並木は日本の「さくら名所100選」にも選定されています。

その桜は、植樹してからもう70年経過していて、日立市が調査したところ、木の内部が腐っていたり、空洞が存在するものがあるんだそうです。

職場の同僚の人からこのクラウドファンディングの話を聞いて、勧められて私も寄付することにしました。

そのクラウドファンディングのページ

花って人間の生活を彩るためには必要なものみたいなので、大切にしている桜に少しお金をかけてみるのも良いかもしれません。



2021/6/20(日)

グリフォンモデリング -[modeling]

指先もボーンで動くようになりました。


画像が飛び出しているように見えるのは、背景付きの画像の他に、モデルだけの透過画像も一緒に作成して、HTMLで合成しているからです。

迫力があって良いでしょ。


父の日その後 -[season]

昨日は深夜1:40ごろまで夜更かししていたので、今朝は10:00すぎまで寝ていましたが、父からの電話で起きました。

父の日で贈った品物が届いたようで、とても喜んでくれていました。よかった。


贈り物を贈るときは、相手の人のキーワードみたいなものを考えて品物を選ぶと良いです。

ちゃんと良いものを贈れるかどうか心配だ…という気持ちも大切かもしれません。

最近はいろいろあるでしょうから、実の父母でなくても、日ごろから両親みたいにお世話になっている人に贈るというのも良いんじゃないでしょうか。

でも父の日は今日で、母の日もこのあいだ過ぎましたから、それは来年の話になりますが…。


コロナウイルスのワクチン接種の話で、父は2回目の接種が済み、母は今日2回目を受けに行っているそうです。

オリンピックも国がやる気でいるけど、(やれば面白いところもあると思うけど)、それで感染が広がるのが心配だ、という話をしました。

国は世界の人々のそれぞれの立場を考えたうえで、オリンピック開催を決めたんでしょうから、苦渋の決断なんでしょうね。


2021/6/19(土)

おにぎり屋さん OPEN -[life]

fig.
語り部がくれたビラ。

職場の女の子が、

「駅前におにぎり屋さんができた、

「おにぎりがこんなにおっきいの!コンビニおにぎりの2倍くらい!

「やわらかく握ってくれて、おいしいの!

と絶賛するので、私も行ってきました。

左の画像リンクをクリックすると 画像を拡大 します。



fig.
▲この2点を電話注文した。

金曜日の夕方に電話注文して、翌日、お昼の11時に取りに行きました。

注文内容は、左の写真の「いただきますセット」と「本日の煮つけ」。

セットのおにぎり2個は「しゃけ」と「焼きみそおにぎり」にしました。

左の画像リンクをクリックすると 画像を拡大 します。



駅前まで歩いて15分くらい。

開店前で、カップルが1組待っていました。

私はすぐ近くのバス・ロータリーの柱(ポール)に寄りかかって待っていましたが、男の人が来て

「並んでるんですか?」

私はあわてて、「あ、はい、すみません」と言ってちゃんと並び、男の人は私の後ろに並びました。

やっぱり、グランドオープン早々、おいしいと評判が広がっているんでしょう。

お昼11時ジャストに開店して、私は注文の品を受け取り、家路につきました。


fig.
▲うわさのおにぎり

いただきまーす。おいしそう。

焼きおに。うん、おいしい。

「本日の煮つけ」は「サバの味噌煮」。棒状4cmくらいの焼きネギも一緒に煮付けてある。んー!ネギがうまい。煮つけも普通においしい。

からあげ、きんぴら、しゃけ。ん!ん゛ー

どれもうまい。おいしい、うん。

(おにぎりの大きさが「コンビニの2倍」ではなく1.5倍くらいなのは、セットだからかな)

左の画像リンクをクリックすると 画像を拡大 します。



fig.
▲レシート

ラーメンのようなパンチのきいたおいしさではなく、 「ちゃんと作りました」という感じの、おとなしいおいしさでした。

満足した。よかった。900円

おいしかったよ とにかく。

左の画像リンクをクリックすると 画像を拡大 します。



職場の語り部は、「私は食べることしか楽しみがない」と言いますが、どの店がおいしい、今日はどこで何を食べた、というのは、もしパートナーがいたら、連れて行ってあげられるし、パートナーはそういうものを必要としている場合が多いので、食べること、美味しいお店を知っていることはとても良いことだと思います。人生の幸せに直結していますからね。

休日にそういう楽しみをしていました、というお話でした。


2021/6/14(月)

RPGの試作 -[javascript,RPG]

fig.
▲これはなんだ

この3連休中に作っていたプログラムです。

この3連休は、SUPER PC WEEK と称して、いつもの制限をなしに、好きなだけプログラミングをしていました。

このプログラムは RPG の全体的で、基本的なプログラムを簡潔に作ろうとして作ったものです。

結局、時間がなくて完成には至っていません。

タッチ入力には対応していないので、スマートホンでは操作できません。パソコンで試してください。

2022年8月18日追記:1年以上ものあいだ、エラーが出る状態に気付きませんでした。直して動作確認したので動くはずです。


そのプログラムリストです。

class App {//c

constructor( canvas ) {//m

this.cc = canvas.getContext( "2d", { alpha : false } );

canvas.width = 256;

canvas.height = 224;


this.screens = new Array();

this.titleScreen = new TitleScreen();

this.mapScreen = new MapScreen();

this.cmdScreen = new CommandScreen();

this.screens.push( this.titleScreen );

this.screens.push( this.mapScreen );

this.screens.push( this.cmdScreen );


this.anms = new Array();

this.media = {

bgms : new Object(),

sounds : new Object(),

}


//データを各スクリーンで共有

for( let screen of this.screens ) {

screen.anms = this.anms;

screen.media = this.media;

}


//メディアのプリロード

return Promise.all( [

// this.load( "bgm", "field", "_bgm/field.wav" ),

// this.load( "bgm", "town", "_bgm/town.wav" ),

// this.load( "bgm", "battle", "_bgm/battle.wav" ),

this.load( "sound", "pushA", "_sound/pushA.mp3" ),

this.load( "sound", "pushWall", "_sound/pushWall.mp3" ),

this.load( "sound", "walk", "_sound/walk.wav" ),

] ).then( function() {

return this; //Appのインスタンスを返すこと

}.bind( this ) );

}

load( type, title, src ) {//m

return new Promise( function( promiseOK ) {

let object;

console.log( type, title, "loading.." );

switch( type ) {

case "bgm":

object = new Audio();

object.oncanplaythrough = function() {

this.media.bgms[ title ] = object;

object.oncanplaythrough = null;

console.log( "\t", type, title, "loaded." );

promiseOK();

}.bind( this );

object.src = src;

object.load();

break;

case "sound":

object = new Audio();

object.oncanplaythrough = function() {

this.media.sounds[ title ] = object;

object.oncanplaythrough = null;

console.log( "\t", type, title, "loaded." );

promiseOK();

}.bind( this );

object.src = src;

object.load();

break;

}

}.bind( this ) );

}

start() {//m

this.keys = new Array();


requestAnimationFrame( function callback( tm ) {

this.frame();

this.timerId = requestAnimationFrame( callback.bind( this ) );

}.bind( this ) );


onkeydown = function( e ) {

if( this.keys.indexOf( e.which ) == -1 ) {

this.keys.push( e.which ); //キーセンス オン

this.currentScreen.typeKey( e.which );

//キータイプ 実行

}

}.bind( this );


onkeyup = function( e ) {

let idx = this.keys.indexOf( e.which );

if( idx > -1 ) this.keys[ idx ] *= -1;

//キーセンス オフ予定にする(同じ数の負値)

}.bind( this );


this.mainStory();

}


async mainStory() {//m

//タイトル画面

this.currentScreen = this.titleScreen;

await this.titleScreen.waitEnd();


//マップ画面

this.currentScreen = this.mapScreen;

this.mapScreen.playerMapMoveTo( App.worldmap, 0, 0, true );

await this.mapScreen.waitEnd();



}


stop() {//m

cancelAnimationFrame( this.timerId );

}

frame() {//m

//キーセンス 処理

for( let i = this.keys.length - 1; i >= 0; i-- ) {

let key = this.keys[ i ];

this.currentScreen.senseKey( Math.abs( key ) );

//キーセンス 実行


//check. キーセンスオフ予定ならキーセンス オフ

if( key < 0 ) this.keys.splice( i, 1 );

}


//アニメ処理

for( let i = this.anms.length - 1; i >= 0; i-- ) {

if( ! this.anms[ i ]() ) this.anms.splice( i, 1 );

}


this.draw( this.cc );


//ストーリー処理

if( this.currentScreen.readStory() ) {

//tweak.

this.keys.length = 0;

}

}

draw( cc ) {//m

this.currentScreen.draw( cc );

}

}//App



class Screen {//c

constructor() {//m

this.actions = new Array();

}

waitEnd() {//m

return new Promise( function( promiseOK ) {

this.promiseOK = promiseOK;

/*

このクラスの外のプログラムで

このクラスのwaitEnd()をawait付けて呼び出すと

外のプログラムはその時点で停止する。

このクラスの中のあるタイミングでpromiseOK()が実行されると、

外のプログラムの停止が解除される。

たとえば、

「はい/いいえ」の入力を待つために

プログラム(ゲームイベント)を停止する。

入力が終わったらpromiseOK()を実行し、

停止していたプログラムが再開される。

*/

}.bind( this ) );

}

senseKey( key ) {

}

typeKey( key ) {

}

readStory() {

}

draw( cc ) {

}

}


class CommandScreen extends Screen {//c

constructor() {

super();


}

typeKey( key ) {

this.media.sounds.pushA.play();

this.promiseOK();

}

}


class MapScreen extends Screen {//c

/*

マップの表示という意味ではなく、

フィールド画面の総合的処理という意味。

*/

constructor() {//m

super();


this.cellSize = 16;

this.cols = 17;

this.rows = 15;

this.colsHalf = Math.floor( this.cols / 2 );

this.rowsHalf = Math.floor( this.rows / 2 );

this.lock = false;

this.tweakX = 0;

this.tweakY = 0;


}//constructor

treatX( x ) {

//tweak. ループ時は範囲外の値を範囲内に調整する

if( this.cfg.maploop ) {

if( x < 0 )

x += this.mapWidth;

else if( x >= this.mapWidth )

x -= this.mapWidth;

}

return x;

}

treatY( y ) {

//tweak. ループ時は範囲外の値を範囲内に調整する

if( this.cfg.maploop ) {

if( y < 0 )

y += this.mapHeight;

else if( y >= this.mapHeight )

y -= this.mapHeight;

}

return y;

}

typeKey( key ) {//m

if( key == 32 ) {


}

}

senseKey( key ) {//m

//check.

if( this.lock ) return;


let addX = ( key == 39 ) - ( key == 37 );

let addY = ( key == 40 ) - ( key == 38 );


//check. 移動先は壁

let sx = this.treatX( this.sx + addX );

let sy = this.treatY( this.sy + addY );

if( this.mapBits[ sy ] )

if( this.mapBits[ sy ][ sx ] )

if( this.cfg.walls.indexOf( this.mapBits[ sy ][ sx ] ) > -1 ) {

this.actionsPush( sx, sy, "push" );

this.media.sounds.pushWall.play();

return;

}


//スクロールについて

if( addX || addY ) {

let anm;

let counter = 0;

anm = function() {

//1ドットスクロール

counter ++;

this.tweakX -= addX;

this.tweakY -= addY;

//tweakX,Yが1ドットスクロールの主要のしくみ

//それについてはthis.draw()を参照してください。


//check. スクロールの終了

if( counter == this.cellSize ) {

this.lock = false;

this.tweakX = 0;

this.tweakY = 0;


this.sx = this.treatX( this.sx + addX );

this.sy = this.treatY( this.sy + addY );



//check. マップ外に乗ったストーリー発生

if( ! this.cfg.maploop )

if( this.sx < 0 || this.sx >= this.mapWidth

|| this.sy < 0 || this.sy >= this.mapHeight ) {

this.actionsPush( "outer,got_on" );

return;

}


//座標に乗ったストーリー発生

this.actionsPush( this.sx, this.sy, "got_on" );


return false; //アニメ(スクロール)終了の意

}//if スクロール終了

return true; //アニメ(スクロール)継続の意

}.bind( this );

//アニメ(スクロール)開始

this.anms.push( anm );

this.lock = true;

}//if 一歩移動

}//senseKey


actionsPush() {//m

this.actions.push( Array.from( arguments ).join( "," ) );

}


playerMapMoveTo( mapdatafunc ) {//m


//check. 

// if( this.bgm ) {

// this.bgm.pause();

// this.bgm.currentTime = 0;

// }


let commentData = mapdatafunc.toString()

.match( /\/\*[\r\n]+([\s\S]+)[\r\n]+\*\// )[ 1 ];

this.cfg = mapdatafunc();


this.mapBits = new Array();

this.stories = new Object();


//マップデータを読み取る ここから

let lines = commentData.split( /\r\n/ );


for( let y = 0; y < lines.length; y++ ) {

let line = lines[ y ];

let bits = new Array();

for( let x = 0; x < line.length; x++ ) {

let bit = line.substr( x, 1 );

//check. 半角文字のときはストーリー設定あり

if( bit.match( /[0-9a-z ]/i ) ) {

let id = bit + line.substr( x + 1, 1 );

id = id.replace( / /, "" );

//check. エラー

if( ! this.cfg.points[ id ] ) {

alert( "マップ上に配置されたストーリー識別子が、ストーリー定義されていません。id:" + id );

bits.push( "!" );

continue;

}


//作業:ストーリーを座標情報をつけて複写

let stories = this.cfg.points[ id ].stories;

for( let key in stories ) {

let newkey = [ x, y, key ].join( "," );

this.stories[ newkey ] = stories[ key ];

}


//作業:半角文字をマップデータに直す

bit = this.cfg.points[ id ].bit;


//作業:座標情報をセット

this.cfg.points[ id ].x = x;

this.cfg.points[ id ].y = y;


x++;

}

bits.push( bit );

}

this.mapBits.push( bits );

}//for

//マップデータを読み取る ここまで


//BGM

// if( this.cfg.bgmTitle ) {

// this.bgm = this.media.bgms[ this.cfg.bgmTitle ];

// this.bgm.loop = true;

// this.bgm.play();

// }


//マップの外に出たときのストーリーをセット

if( ! this.cfg.maploop ) {

let story = this.cfg.points.outer.stories.got_on;

this.stories[ "outer,got_on" ] = story;

}


//sx,sy について

let argArray = Array.from( arguments );

let lastArg;

if( typeof argArray[ argArray.length - 1 ] == "boolean" ) {

lastArg = argArray.pop();

}

let argtypes = argArray.map( arg => ( typeof arg ).substr( 0, 1 ) ).join( "" );

if( argtypes == "fs" ) {

let id = arguments[ 1 ];

this.sx = this.cfg.points[ id ].x;

this.sy = this.cfg.points[ id ].y;

} else {

this.sx = arguments[ 1 ];

this.sy = arguments[ 2 ];

}


//---歩く音

if( ! lastArg ) {

this.media.sounds.walk.play();

}



this.mapWidth = this.mapBits[ 0 ].length;

this.mapHeight = this.mapBits.length;


this.debugStories = new Object();

Object.keys( this.stories ).map( function( key ) {

let tokens = key.split( /,/ );

let x = tokens[ 0 ];

let y = tokens[ 1 ];

//check.

if( ! this.debugStories[ y ] ) this.debugStories[ y ] = new Object();

this.debugStories[ y ][ x ] = true;

}.bind( this ) );


}//playerMapMoveTo

readStory() {//m

for( let i = this.actions.length - 1; i >= 0; i-- ) {

let action = this.actions[ i ];

let story = this.stories[ action ];

if( story && ! story.isRead ) {

story.execute.call( this );

story.isRead = true;

return true; //ストーリー実行しましたの意味

//移動中にストーリー発生で、もしalert()が行われると

//キー入力のkeyupが行われず、ストーリー後

//押し続けになってしまうことから

}

}

this.actions.length = 0;

}

draw( cc ) {//m

cc.clearRect( 0, 0, 256, 224 );


//this.sx,this.syが画面中央に来るように描画開始座標を調整

let sx = this.sx - this.colsHalf + this.mapWidth;

let sy = this.sy - this.rowsHalf + this.mapHeight;

//+ this.mapWidth,Heightは、下の計算で % this.mapWidth,Height

//としたときに負値になるのを避けるため。


cc.save();

cc.translate( -this.cellSize / 2 + this.tweakX, -this.cellSize / 2 + this.tweakY );

//tweakX,Yが描画位置を1ドットずつずらすので

//きれいに1ドットスクロールしてるようにみえる


cc.font = this.cellSize + "px ''";

cc.fillStyle = "rgb(0,255,0)";

for( let row = 0; row < this.rows; row++ ) {

let y = ( sy + row ) % this.mapHeight; //端を超えると反対側に

let gy = row * this.cellSize;

for( let col = 0; col < this.cols; col++ ) {

let x = ( sx + col ) % this.mapWidth; //端を超えると反対側に

let bit = this.mapBits[ y ][ x ];

let gx = col * this.cellSize;

cc.fillText( bit, gx, gy + this.cellSize );


//debug.

if( 1 )

if( this.debugStories[ y ] )

if( this.debugStories[ y ][ x ] ) {

cc.strokeStyle = "red";

cc.strokeRect( gx, gy, this.cellSize, this.cellSize );

}

}

}

cc.restore();


//プレイヤーキャラ描画

cc.fillStyle = "red";

cc.font = this.cellSize + "px ''";

let gx = ( cc.canvas.width - this.cellSize ) / 2;

let gy = ( cc.canvas.height - this.cellSize ) / 2 + this.cellSize;

cc.fillText( "Ω", gx, gy );


}//draw

}//MapScreen


class TitleScreen extends Screen {//c

constructor() {//m

super();

this.test = 0;

this.test2 = 0;

}

typeKey( key ) {//m

this.media.sounds.pushA.play();

this.promiseOK();

}

senseKey( key ) {//m

switch( key ) {

case 37:

this.test -= 0.01;

break;

case 39:

this.test += 0.01;

break;

default:

console.log( key );

}

}

draw( cc ) {//m

cc.clearRect( 0, 0, 256, 224 );


cc.fillStyle = "white";

cc.fillText( "hit any key", 128, 112 );

}

}//TitleScreen


App.worldmap = function() {/*

●〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃¥¥¥▲

¥山山山山山〃~~~~~~¥¥〃〃〃〃〃〃〃〃〃¥¥山山山¥

¥山山山〃〃〃〃~~~〃〃〃〃〃〃〃〃〃〃〃〃¥¥山山山¥¥

¥山山〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃¥¥山山山¥¥〃

¥¥〃〃1 〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃¥¥山山山¥¥〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃¥¥山山山¥¥〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃¥¥¥山山山¥¥〃〃〃

〃〃〃〃〃〃〃〃〃山山〃〃〃〃〃〃〃¥¥¥山山山¥¥〃〃〃〃

〃〃〃〃〃〃〃山山山〃〃〃〃〃〃〃¥¥¥山山山¥¥〃〃〃〃〃

〃〃〃〃山山山山山〃〃〃〃〃〃〃〃¥¥山山山山〃〃〃〃〃〃〃

〃〃〃山山山〃〃〃〃〃〃〃〃¥〃〃¥山山山山山〃〃〃〃〃〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃¥¥¥〃〃〃〃山山山山山山〃〃〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃¥〃〃〃〃〃山山山山山山山〃〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃山山山山

山山山〃〃〃~~~〃〃〃〃〃〃〃〃〃〃山山〃〃〃〃〃〃〃〃〃

山山〃〃~~~~¥¥〃〃〃〃〃〃〃〃山山山山〃〃〃〃〃〃〃山

山〃〃~~~¥¥¥〃〃〃〃〃〃〃〃山山山山山山山〃〃〃〃〃山

〃〃〃〃¥¥¥¥〃〃〃〃〃〃〃〃〃山山山山山山山山〃〃〃〃〃

〃〃〃〃〃〃¥¥¥〃〃〃〃〃〃〃山山山山山山山山山山~~〃〃

〃〃〃〃〃〃〃¥¥¥〃〃〃〃〃〃山山山山山~~~~~~〃〃〃

〃〃〃〃〃〃〃¥¥¥〃〃〃〃〃〃〃〃〃〃~~~~~~〃〃〃〃

〃〃〃〃¥¥¥¥¥¥¥〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃

〃〃〃¥¥¥¥¥¥¥¥〃〃〃〃〃〃〃〃~~~~~〃〃〃〃〃〃

〃〃¥¥¥¥¥¥〃〃〃〃〃〃〃〃〃~~~~~〃〃〃〃〃〃〃〃

〃¥¥¥¥¥〃〃〃〃〃〃〃〃〃〃~~~~〃〃〃〃〃〃〃〃〃〃

〃〃¥¥〃〃〃〃〃〃〃〃〃〃~~~~~〃〃〃〃〃〃〃¥¥〃〃

〃〃〃〃〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃¥¥〃〃〃

〃〃〃〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃

○〃〃〃〃〃〃〃〃~~~~~~~〃〃〃〃〃〃〃〃〃〃〃〃〃△

*/

return {

maploop : true,

bgmTitle : "field",

walls : "~",

points : {

"1" : {

bit : "凸",

stories : {

"got_on" : {

execute : function( e ) {

this.playerMapMoveTo( App.townmap, "s" );

},

},

},

},

},

}//return

}//worldmap


App.townmap = function() {/*

〃〃〃〃〃〃〃〃・・〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃

〃■■■■■♧〃・・〃♧■■■■■■■〃〃〃〃〃〃〃〃〃〃〃

〃■・・・■♧〃・・〃♧■■■■■■■〃〃〃〃〃〃〃〃〃〃〃

〃■・Ω・■♧〃・・〃♧■■■■■■■〃〃〃〃〃〃〃〃〃〃〃

〃■■□■■♧〃・・〃♧■■■П■■■〃〃〃〃〃〃〃〃〃〃〃

〃〃〃・〃〃〃〃・・〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃

s ・・・・・・・・・〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃

・・・・・・・・・・〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃

〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃

*/

return {

maploop : false,

bgmTitle : "town",

walls : "■□",

points : {

outer : {

bit : "〃",

stories : {

"got_on" : {

execute : function( e ) {

this.playerMapMoveTo( App.worldmap, "1" );

},

},

},

},

"s" : {

bit : "・",

},

},

}//return

}//townmap



2021/6/13(日)

父の日 -[season]

父の日の贈り物を通販で購入しました。6/20(父の日)に届く予定です。

ドリップコーヒーの飲み比べできるセットと、そのお茶うけのお菓子。

でもそれだけだと、さびしいんじゃないかと思って、悩んだ挙句、花も追加しました。

これらを合わせると、結構な値段になりますが、たとえ10円だろうと、100万円だろうと、人の気持ちには金額はあまり関係はないでしょう。

また、クリッククリックで簡単に商品を贈っても気持ちは伝わらない、というのも かなり私の中で大きく問題になっていますが、花を追加したことで、花はどこか「歓迎」のような印象もあるし、良いかな?と思います。

ちょっと贈りすぎた感もありますが、私としてはこれでベストです。これ以下はないし、これ以上もありません。

商品一つ贈る場合は、直筆でメッセージを添えれば、気持ちは伝わりますが、今回は通販でそれができないので、こんな形になりました。


私は子供のころは父の日、母の日なんて気にも留めず、プレゼントをもらうことばかり目当てにしていましたが、最近は特に何も求めず、こちらから贈るようになりました。

義理の家族が父の日、母の日に何かを贈る習慣を持っていて、そこから影響を受けた部分と、あと、父母の年齢が高くなっていて、そういうものが必要な年頃ではないかと思うのと、…あとなんだろうな、子供のころにいろいろ迷惑をかけたとかいろいろやってもらったとか、そういう思いもあるけど、やっぱり相手の気持ちかな?疎遠ではなく身近だから。身近なので相手の気持ちがわりと近くにあって、見過ごせないのかな?

私のパソコンの力、絵の力に、創作の力、それらは父母がいなければ成立しませんでした。

疎遠であっても、この気持ちがあるなら、贈るかなぁ…?

これを読んでいる訪問者の方も、ご家族が身近ならば、何かの日に何かを贈ってみたらいかがでしょうか。


…あれ? 「絵を描くには?」と日ごろから考えていますが、上の言葉からすると「絵を描くのに必要なのは父母」と言っていますよね?

絵を描くために必要な条件はいろいろあるけど、その中に「父母」があるのであって、人それぞれ必要な条件は異なる、かな。

ご両親がいなくても絵がめっちゃ上手な人はたくさんいるでしょうから。

私は絵を描くための条件に「父母」がいるということです。


(訪問者のどんなニーズと この記事がつながるか)


2021/6/6(日)

グリフォンモデリング -[modeling]

RPG に登場させるモンスターとして「グリフォン」を作成しています。

身体にボーンをしこんで、だいぶポーズが取れるようになりました。

あとは、指先にもボーンを仕込んで広げられるようにして、首すじあたりの動きを調整すれば、ボーン(関節)としては完成です。

それが終わったら、ワシ部分、ライオン部分がそれぞれ「動物」そのままの色なので、モンスターらしい色に変更したら、すべて完成です。


(訪問者のどんなニーズと この記事がつながるか)


2021/6/5(土)

setInterval() 以外のアニメーションプログラム検証 -[JavaScript]

JavaScript でアニメーションを行う場合、初歩としては、setInterval() を使うと思います。

setInterval() は、定期的に指定の関数を実行してくれる関数です。

実行されるたびに絵を描く位置を変えればアニメになります。とてもお手軽です。


しかし、JavaScript には他にも requestAnimationFrame() というものがあり、これを使ってもアニメーションすることができます。

requestAnimationFrame() は「アニメするために」用意された特別な関数です。

setInterval() を使ってアニメーションすると、ときどき ぎこちない動きになるのに対し、requestAnimationFrame() ではその ぎこちなさが ほとんどありません。

fig.
▲setInterval() によるアニメとrequestAnimationFrame() によるアニメを比べることができる JavaScript

その動きの違いを検証しようと思って、左のような JavaScript を作成しました。


左のリンクを開くと、ボールがいっぱい動くアニメーションが開始されます。

下部の設定欄で、setInterval() と requestAnimationFrame() を切り替えることができます。

表示速度(fps)や、ボールの数も切り替えられるので、わざと重い処理にしてみてください。setInterval() のぎこちなさや、requestAnimationFrame() でのその解決を 比べて見ることができます。

どうぞお試しください。



コンピューターの画面というのは、パラパラマンガみたいに、動きのある絵を高速で切り替えて表示しています。

setInterval() はそのパラパラのタイミングに関係なく、とにかく決められた時間に関数を実行するので、絵を切り替えている途中で実行されてしまうこともあり、その際にぎこちなくなります。

requestAnimationFrame() はその点が考慮されていて、パラパラのタイミングに合わせて関数を実行してくれます。そのため常に滑らかに表示されます。

高品位のアニメーションを行いたい場合は、setInterval() ではなく、requestAnimationFrame() を使うとよいです。


以下のプログラムリストの、switch文の「case 0:」が setInterval() の場合のプログラム、

「case 5:」が requestAnimationFrame() の場合のプログラムです。

<html><!--ESCAPEPROCESS-->

<head>

<meta content="text/html; charset=UTF-8" http-equiv="content-type">

<script>

console.clear();

onclick = location.reload.bind( location );

function multi( func ) {

let txt = func.toString().match( /\/\*([\s\S]+)\*\// )[ 1 ];

let tabs = txt.split( /\n/ ).map( line => line.match( /^\t*/ )[ 0 ] );

let tabcnt = tabs[ tabs.length - 1 ].length;

let re = new RegExp( "^\t{" + tabcnt + "}", "gm" );

txt = txt.replace( re, "" ).replace( /^\n|\n+$/, "" );

return txt;

}

</script>

<script>

function onloadx() {

//ctrl

if( typeof top.ctrl === "undefined" ) {

type = 0;

timerMs = 1000 / 60;

ballsLen = 50;

} else {

type = top.ctrl.type;

timerMs = top.ctrl.timerMs;

ballsLen = top.ctrl.ballsLen;

}

let stat = document.getElementById( "stat" );

let html = "【JavaScriptアニメ2種類を比べよう】<BR>アニメ種類:";

html += type == 0 ? "setInterval()" : "requestAnimationFrame()";

html += "<BR>";

html += "fps: " + Math.round( 1000 / timerMs ) + "<BR>";

html += "ボール数: " + ballsLen + "<BR>";

stat.innerHTML = html;


//ボールの作成 ここから

let colors = [

"lightblue",

"magenta",

"pink",

"lightgreen",

"blue",

"yellow",

];

let addGeta = 4;

let addLeng = 8;

size = 128;

balls = new Array();

for( let i = 0; i < ballsLen; i++ ) {

let ball = document.createElement( "div" );

//スタイル一括指定

ball.setAttribute( "style", multi( function() {/*

position : absolute;

opacity : 0.5;

*/} ) );


//ボール形状

ball.style.width = size + "px";

ball.style.height = size + "px";

ball.style.borderRadius = size / 2 + "px";


//ボール色

let idx = Math.floor( Math.random() * colors.length );

ball.style.backgroundColor = colors[ idx ];


//ボール位置

ball.style.left = Math.random() * window.innerWidth - size / 2 + "px";

ball.style.top = Math.random() * window.innerHeight - size / 2 + "px";


//ボール移動方向

ball.addX = addGeta + Math.random() * addLeng;

ball.addY = addGeta + Math.random() * addLeng;

ball.addX *= Math.random() < 0.5 ? 1 : -1;

ball.addY *= Math.random() < 0.5 ? 1 : -1;

console.log( i, ball.addX, ball.addY );


document.body.appendChild( ball );

balls.push( ball );

}

//ボールの作成 ここまで


//アニメーションの実行

let step;

let start = null;

let start2 = new Date().getTime() - timerMs;


switch( type ) {


case 0: //setInterval() を使用した場合

timerId = setInterval( frame, timerMs );

//クリックで終了

onclick = function() {

clearInterval( timerId );

}

break;


case 1: //mozilla の mdn の requestAnimationFrame() の説明ページのサンプル

step = function( timestamp ) {

if( ! start ) start = timestamp;

let progress = timestamp - start;

frame();

if( progress < 2000 ) {

window.requestAnimationFrame( step );

}

}

window.requestAnimationFrame( step );

break;


case 2: //それを簡略化したもの

step = function( timestamp ) {

frame();

window.requestAnimationFrame( step );

}

window.requestAnimationFrame( step );

break;


case 3: //さらに関数名stepを省略したもの

window.requestAnimationFrame( function callback( timestamp ) {

frame();

window.requestAnimationFrame( callback );

} );

break;


case 4: //さらにアニメを停止する機能を盛り込んだもの

window.requestAnimationFrame( function callback( timestamp ) {

frame();

timerId = window.requestAnimationFrame( callback );

} );

//クリックで終了

onclick = function() {

cancelAnimationFrame( timerId );

}

break;


case 5: //さらに setInterval() のように定期の時間を指定できるようにしたもの

window.requestAnimationFrame( function callback() {

//check. 指定時間経過

if( new Date().getTime() - start2 >= timerMs ) {

frame();

start2 = new Date().getTime();//次の計測用

}


timerId = window.requestAnimationFrame( callback );

} );

//クリックで終了

onclick = function() {

cancelAnimationFrame( timerId );

}

break;


}

}


function frame() {

for( let ball of balls ) {

let style = getComputedStyle( ball );

let x = Number( style.left.replace( /px/, "" ) ) + size / 2;

let y = Number( style.top.replace( /px/, "" ) ) + size / 2;

x += ball.addX;

y += ball.addY;

//check.

if( x <= 0 || x >= innerWidth ) ball.addX *= -1;

if( y <= 0 || y >= innerHeight ) ball.addY *= -1;

ball.style.left = x - size / 2 + "px";

ball.style.top = y - size / 2 + "px";

}

}

</script>

<style>

</style>

</head>

<body onload="onloadx()" style="

overflow : hidden;

">

<div id="stat" style="

position : absolute;

z-index : 255;

background-color : rgba( 0,0,0,0.5 );

color : white;

padding : 1em;

"></div>

</body>

</html>



(訪問者のどんなニーズと この記事がつながるか)


2021/6/3(木)

ドラクエの 3年前に発売された国産RPG「ポイボス」 -[RPG]

まだあったRPGのルーツ

ついこのあいだ、5月14日 のことですが、「ファミコンのゲームソフトの細かい年表が見たい」と思って、ウェブ検索をかけました。

検索結果にウィキペディアの「コンピュータゲームの歴史」というページがヒットし、そのページを見ていたところ、「ポイボス。日本製ロールプレイングゲーム~」という言葉が目に留まりました。(歴史の1983年11月の項目)

私は以前から綿密にロールプレイングゲームの歴史を調べたつもりでいましたが、「ポイボス」は取りこぼしていたようです。

ロールプレイングゲームの歴史

タイトル(RPG種類)生産国デバイス
1974年
ダンジョンズ&ドラゴンズ(卓上RPG) アメリカ 卓上


m199h(現存せず失われた最初のコンピュータRPG) アメリカ メインフレーム
1975年
pedit5(現存する最初期のコンピュータRPG) アメリカ メインフレーム


dnd(RPG) アメリカ メインフレーム
1976年
ダンジョン(RPG) アメリカ メインフレーム
1979年
Akarabeth(RPG) アメリカ パソコン
1980年
ウルティマ(RPG) アメリカ パソコン
1981年
ウィザードリィ(RPG) アメリカ パソコン
1982年



1983年 11月 ポイボス(RPG) 日本 パソコン

12月 ぱのらま島(ファルコム初のRPG) 日本 パソコン


ボコスカウォーズ(シミュレーションRPG) 日本 パソコン
1984年 1月 ザ・ブラックオニキス(RPG) 日本 パソコン

3月 夢幻の心臓(RPG) 日本 パソコン

6月 ドルアーガの塔(アクションRPG、アーケード) 日本 アーケード

11月 ドラゴンスレイヤー(RPG) 日本 パソコン

12月 ハイドライド(RPG) 日本 パソコン
1985年 8月 ドルアーガの塔(ファミコン初の RPG) 日本 ファミコン

10月 ザナドゥ(アクションRPG) 日本 パソコン

12月 ハイドライドII(アクションRPG) 日本 パソコン
1986年 5月 ドラクエ1(RPG) 日本 ファミコン

こうしてみると、ドラクエ以前にも RPG はたくさんあったんですねぇ。


RPGの面白さの秘密

ポイボスはちょっとダウンロードして遊んでみましたが、確かな面白さがあって、2時間程度ハマってしまいました。

その面白さの秘密とは、

まずポイボスをプレイしてみるとキャラクター1名だとかなり不利でどうにもなりません。

敵にまったく勝てずに、すぐにゲームオーバー。

武器がないとかなり不利。

ただし、こまめにセーブができるので、ちょっとでも進展(成長)があったらすぐにセーブするようにします。

仲間を作ると敵に勝てるようになります。

また、武器を手に入れても勝てるようになります。

そうやって、できなかったことができるようになると、「面白い!」と思います。


そして、以上のプロセスを経て初めて「次」を求める自分に気が付きます。

次というのは、ゲームの「ストーリー」や、取り組む「謎」です。

RPGってもしかしたら、成長のプロセスで楽しむことがまずは必要で、ストーリーや冒険は二の次なのかもしれない…と思いました。


ダウンロードしたくてもリンク切れ

「ポイボス Windows版」を作った人のページ

ポイボスのメーカー(販売元から権利譲渡してもらっている元スタッフの方)から公開許可をもらって 自作のWindows版を公開している方のページです。

ただし、その Windows版のダウンロードのリンクはリンク切れしています。残念!


(訪問者のどんなニーズと この記事がつながるか)